package com.webank.openledger.contracts;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.fisco.bcos.sdk.abi.FunctionEncoder;
import org.fisco.bcos.sdk.abi.FunctionReturnDecoder;
import org.fisco.bcos.sdk.abi.TypeReference;
import org.fisco.bcos.sdk.abi.datatypes.Address;
import org.fisco.bcos.sdk.abi.datatypes.Function;
import org.fisco.bcos.sdk.abi.datatypes.Type;
import org.fisco.bcos.sdk.abi.datatypes.Utf8String;
import org.fisco.bcos.sdk.abi.datatypes.generated.Bytes32;
import org.fisco.bcos.sdk.abi.datatypes.generated.StaticArray4;
import org.fisco.bcos.sdk.abi.datatypes.generated.Uint256;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple1;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple2;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.contract.Contract;
import org.fisco.bcos.sdk.crypto.CryptoSuite;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.model.CryptoType;
import org.fisco.bcos.sdk.model.TransactionReceipt;
import org.fisco.bcos.sdk.model.callback.TransactionCallback;
import org.fisco.bcos.sdk.transaction.model.exception.ContractException;

@SuppressWarnings("unchecked")
public class Term extends Contract {
    public static final String[] BINARY_ARRAY = {"608060405234801561001057600080fd5b50604051602080610da28339810180604052810190808051906020019092919050505061003b6100de565b604051809103906000f080158015610057573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506100ee565b60405161011080610c9283390190565b610b95806100fd6000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063124b65b41461006757806338cc4831146100fe5780638662f51f14610155578063fa47d9c914610180575b600080fd5b34801561007357600080fd5b5061007c61022c565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100c25780820151818401526020810190506100a7565b50505050905090810190601f1680156100ef5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b34801561010a57600080fd5b506101136102d9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016157600080fd5b5061016a6102e1565b6040518082815260200191505060405180910390f35b34801561018c57600080fd5b50610216600480360381019080803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929080608001906004806020026040519081016040528092919082600460200280828437820191505050505091929192905050506103a8565b6040518082815260200191505060405180910390f35b600060606001546002808054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102ca5780601f1061029f576101008083540402835291602001916102ca565b820191906000526020600020905b8154815290600101906020018083116102ad57829003601f168201915b50505050509050915091509091565b600030905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636d4ce63c6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15801561036857600080fd5b505af115801561037c573d6000803e3d6000fd5b505050506040513d602081101561039257600080fd5b8101908080519060200190929190505050905090565b6000806103b4836106b2565b90508073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156104a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001807f5465726d3a6f6e6c792070726f6a656374206f776e657220697320617574686f81526020017f72697a65642e000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b600084511180156104b3575060408451105b151561054d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001807f7465726d6e616d65202073686f756c64206265206e6f74206e756c6c20616e6481526020017f206c657373207468616e203634206c6f6e67000000000000000000000000000081525060400191505060405180910390fd5b6105556107af565b60018054016001819055508360029080519060200190610576929190610a44565b506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636d4ce63c6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1580156105fc57600080fd5b505af1158015610610573d6000803e3d6000fd5b505050506040513d602081101561062657600080fd5b81019080805190602001909291905050506003819055506060604051908101604052808581526020016003548152602001600354815250600560006001548152602001908152602001600020600082015181600001908051906020019061068e929190610ac4565b50602082015181600101556040820151816002015590505060015491505092915050565b6000806000806000808660006004811015156106ca57fe5b602002015194508660016004811015156106e057fe5b602002015193506106f484600019166109ae565b925086600260048110151561070557fe5b6020020151915086600360048110151561071b57fe5b60200201519050600185848484604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af1158015610799573d6000803e3d6000fd5b5050506020604051035195505050505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166335a5cfcb6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15801561083657600080fd5b505af115801561084a573d6000803e3d6000fd5b505050506040513d602081101561086057600080fd5b810190808051906020019092919050505090506001805411156109ab576060604051908101604052806005600060015481526020019081526020016000206000018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109355780601f1061090a57610100808354040283529160200191610935565b820191906000526020600020905b81548152906001019060200180831161091857829003601f168201915b50505050508152602001600560006001548152602001908152602001600020600101548152602001828152506005600060015481526020019081526020016000206000820151816000019080519060200190610992929190610ac4565b5060208201518160010155604082015181600201559050505b50565b6000806000809150600090505b6020811015610a3a576001810160200360080260020a84826020811015156109df57fe5b1a7f0100000000000000000000000000000000000000000000000000000000000000027f0100000000000000000000000000000000000000000000000000000000000000900460ff16028201915080806001019150506109bb565b8192505050919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610a8557805160ff1916838001178555610ab3565b82800160010185558215610ab3579182015b82811115610ab2578251825591602001919060010190610a97565b5b509050610ac09190610b44565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610b0557805160ff1916838001178555610b33565b82800160010185558215610b33579182015b82811115610b32578251825591602001919060010190610b17565b5b509050610b409190610b44565b5090565b610b6691905b80821115610b62576000816000905550600101610b4a565b5090565b905600a165627a7a72305820e9b98bac02552de5963afe50ba0123fc63fbe3d5d6f57e9f0fe0bebb71b17fc60029608060405234801561001057600080fd5b50600160008190555060e9806100276000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806335a5cfcb14604e5780636d4ce63c146076575b600080fd5b348015605957600080fd5b506060609e565b6040518082815260200191505060405180910390f35b348015608157600080fd5b50608860a7565b6040518082815260200191505060405180910390f35b60008054905090565b60006001600054016000819055506000549050905600a165627a7a72305820d3c965ab3593a2ba3b7c26c74d9e0fb297bc88b9762c86ea21268d99e5c3eed30029"};

    public static final String BINARY = String.join("", BINARY_ARRAY);

    public static final String[] SM_BINARY_ARRAY = {"608060405234801561001057600080fd5b50604051602080610da28339810180604052810190808051906020019092919050505061003b6100de565b604051809103906000f080158015610057573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506100ee565b60405161011080610c9283390190565b610b95806100fd6000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806311567c42146100675780631bf72735146100be5780633de67ccf14610155578063bd332ffe14610201575b600080fd5b34801561007357600080fd5b5061007c61022c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156100ca57600080fd5b506100d3610234565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156101195780820151818401526020810190506100fe565b50505050905090810190601f1680156101465780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b34801561016157600080fd5b506101eb600480360381019080803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929080608001906004806020026040519081016040528092919082600460200280828437820191505050505091929192905050506102e1565b6040518082815260200191505060405180910390f35b34801561020d57600080fd5b506102166105eb565b6040518082815260200191505060405180910390f35b600030905090565b600060606001546002808054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102d25780601f106102a7576101008083540402835291602001916102d2565b820191906000526020600020905b8154815290600101906020018083116102b557829003601f168201915b50505050509050915091509091565b6000806102ed836106b2565b90508073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156103da576040517fc703cb120000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001807f5465726d3a6f6e6c792070726f6a656374206f776e657220697320617574686f81526020017f72697a65642e000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b600084511180156103ec575060408451105b1515610486576040517fc703cb120000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001807f7465726d6e616d65202073686f756c64206265206e6f74206e756c6c20616e6481526020017f206c657373207468616e203634206c6f6e67000000000000000000000000000081525060400191505060405180910390fd5b61048e6107af565b600180540160018190555083600290805190602001906104af929190610a44565b506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663299f7f9d6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15801561053557600080fd5b505af1158015610549573d6000803e3d6000fd5b505050506040513d602081101561055f57600080fd5b8101908080519060200190929190505050600381905550606060405190810160405280858152602001600354815260200160035481525060056000600154815260200190815260200160002060008201518160000190805190602001906105c7929190610ac4565b50602082015181600101556040820151816002015590505060015491505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663299f7f9d6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15801561067257600080fd5b505af1158015610686573d6000803e3d6000fd5b505050506040513d602081101561069c57600080fd5b8101908080519060200190929190505050905090565b6000806000806000808660006004811015156106ca57fe5b602002015194508660016004811015156106e057fe5b602002015193506106f484600019166109ae565b925086600260048110151561070557fe5b6020020151915086600360048110151561071b57fe5b60200201519050600185848484604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af1158015610799573d6000803e3d6000fd5b5050506020604051035195505050505050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b4e5591e6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15801561083657600080fd5b505af115801561084a573d6000803e3d6000fd5b505050506040513d602081101561086057600080fd5b810190808051906020019092919050505090506001805411156109ab576060604051908101604052806005600060015481526020019081526020016000206000018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109355780601f1061090a57610100808354040283529160200191610935565b820191906000526020600020905b81548152906001019060200180831161091857829003601f168201915b50505050508152602001600560006001548152602001908152602001600020600101548152602001828152506005600060015481526020019081526020016000206000820151816000019080519060200190610992929190610ac4565b5060208201518160010155604082015181600201559050505b50565b6000806000809150600090505b6020811015610a3a576001810160200360080260020a84826020811015156109df57fe5b1a7f0100000000000000000000000000000000000000000000000000000000000000027f0100000000000000000000000000000000000000000000000000000000000000900460ff16028201915080806001019150506109bb565b8192505050919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610a8557805160ff1916838001178555610ab3565b82800160010185558215610ab3579182015b82811115610ab2578251825591602001919060010190610a97565b5b509050610ac09190610b44565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610b0557805160ff1916838001178555610b33565b82800160010185558215610b33579182015b82811115610b32578251825591602001919060010190610b17565b5b509050610b409190610b44565b5090565b610b6691905b80821115610b62576000816000905550600101610b4a565b5090565b905600a165627a7a7230582012aa6611149ee3c7df69f02d49a3389eabc365dc32eb3a25ea4d7e65756456540029608060405234801561001057600080fd5b50600160008190555060e9806100276000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063299f7f9d14604e578063b4e5591e146076575b600080fd5b348015605957600080fd5b506060609e565b6040518082815260200191505060405180910390f35b348015608157600080fd5b50608860b4565b6040518082815260200191505060405180910390f35b6000600160005401600081905550600054905090565b600080549050905600a165627a7a72305820723ead1e16a09964278301c9daba8e7d7c838b5ffe836a9bfe35f931e33ee0eb0029"};

    public static final String SM_BINARY = String.join("", SM_BINARY_ARRAY);

    public static final String[] ABI_ARRAY = {"[{\"constant\":true,\"inputs\":[],\"name\":\"getTerm\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"},{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getAddress\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"getSeqNo\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"termname\",\"type\":\"string\"},{\"name\":\"sign\",\"type\":\"bytes32[4]\"}],\"name\":\"newTerm\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"projectOwner\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"};

    public static final String ABI = String.join("", ABI_ARRAY);

    public static final String FUNC_GETTERM = "getTerm";

    public static final String FUNC_GETADDRESS = "getAddress";

    public static final String FUNC_GETSEQNO = "getSeqNo";

    public static final String FUNC_NEWTERM = "newTerm";

    protected Term(String contractAddress, Client client, CryptoKeyPair credential) {
        super(getBinary(client.getCryptoSuite()), contractAddress, client, credential);
    }

    public static String getBinary(CryptoSuite cryptoSuite) {
        return (cryptoSuite.getCryptoTypeConfig() == CryptoType.ECDSA_TYPE ? BINARY : SM_BINARY);
    }

    public Tuple2<BigInteger, String> getTerm() throws ContractException {
        final Function function = new Function(FUNC_GETTERM, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}, new TypeReference<Utf8String>() {}));
        List<Type> results = executeCallWithMultipleValueReturn(function);
        return new Tuple2<BigInteger, String>(
                (BigInteger) results.get(0).getValue(), 
                (String) results.get(1).getValue());
    }

    public String getAddress() throws ContractException {
        final Function function = new Function(FUNC_GETADDRESS, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Address>() {}));
        return executeCallWithSingleValueReturn(function, String.class);
    }

    public TransactionReceipt getSeqNo() {
        final Function function = new Function(
                FUNC_GETSEQNO, 
                Arrays.<Type>asList(), 
                Collections.<TypeReference<?>>emptyList());
        return executeTransaction(function);
    }

    public void getSeqNo(TransactionCallback callback) {
        final Function function = new Function(
                FUNC_GETSEQNO, 
                Arrays.<Type>asList(), 
                Collections.<TypeReference<?>>emptyList());
        asyncExecuteTransaction(function, callback);
    }

    public String getSignedTransactionForGetSeqNo() {
        final Function function = new Function(
                FUNC_GETSEQNO, 
                Arrays.<Type>asList(), 
                Collections.<TypeReference<?>>emptyList());
        return createSignedTransaction(function);
    }

    public Tuple1<BigInteger> getGetSeqNoOutput(TransactionReceipt transactionReceipt) {
        String data = transactionReceipt.getOutput();
        final Function function = new Function(FUNC_GETSEQNO, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
        List<Type> results = FunctionReturnDecoder.decode(data, function.getOutputParameters());
        return new Tuple1<BigInteger>(

                (BigInteger) results.get(0).getValue()
                );
    }

    public TransactionReceipt newTerm(String termname, List<byte[]> sign) {
        final Function function = new Function(
                FUNC_NEWTERM, 
                Arrays.<Type>asList(new Utf8String(termname),
                new StaticArray4<Bytes32>(
                        org.fisco.bcos.sdk.abi.Utils.typeMap(sign, Bytes32.class))),
                Collections.<TypeReference<?>>emptyList());
        return executeTransaction(function);
    }

    public void newTerm(String termname, List<byte[]> sign, TransactionCallback callback) {
        final Function function = new Function(
                FUNC_NEWTERM, 
                Arrays.<Type>asList(new Utf8String(termname),
                new StaticArray4<Bytes32>(
                        org.fisco.bcos.sdk.abi.Utils.typeMap(sign, Bytes32.class))),
                Collections.<TypeReference<?>>emptyList());
        asyncExecuteTransaction(function, callback);
    }

    public String getSignedTransactionForNewTerm(String termname, List<byte[]> sign) {
        final Function function = new Function(
                FUNC_NEWTERM, 
                Arrays.<Type>asList(new Utf8String(termname),
                new StaticArray4<Bytes32>(
                        org.fisco.bcos.sdk.abi.Utils.typeMap(sign, Bytes32.class))),
                Collections.<TypeReference<?>>emptyList());
        return createSignedTransaction(function);
    }

    public Tuple2<String, List<byte[]>> getNewTermInput(TransactionReceipt transactionReceipt) {
        String data = transactionReceipt.getInput().substring(10);
        final Function function = new Function(FUNC_NEWTERM, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {}, new TypeReference<StaticArray4<Bytes32>>() {}));
        List<Type> results = FunctionReturnDecoder.decode(data, function.getOutputParameters());
        return new Tuple2<String, List<byte[]>>(

                (String) results.get(0).getValue(), 
                convertToNative((List<Bytes32>) results.get(1).getValue())
                );
    }

    public Tuple1<BigInteger> getNewTermOutput(TransactionReceipt transactionReceipt) {
        String data = transactionReceipt.getOutput();
        final Function function = new Function(FUNC_NEWTERM, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
        List<Type> results = FunctionReturnDecoder.decode(data, function.getOutputParameters());
        return new Tuple1<BigInteger>(

                (BigInteger) results.get(0).getValue()
                );
    }

    public static Term load(String contractAddress, Client client, CryptoKeyPair credential) {
        return new Term(contractAddress, client, credential);
    }

    public static Term deploy(Client client, CryptoKeyPair credential, String projectOwner) throws ContractException {
        String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new Address(projectOwner)));
        return deploy(Term.class, client, credential, getBinary(client.getCryptoSuite()), encodedConstructor);
    }
}
